package com.ipan.builder;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.hibernate.cfg.Environment;

import com.ipan.builder.util.BdClassLoaderUtils;
import com.ipan.builder.util.BdFileUtils;
import com.ipan.builder.util.BdStringUtils;

/**
 * 模板配置信息类
 * 
 * @author iPan
 * @version 2013-11-27
 */
public class BuilderCfg {
	public static final String PROJECT_CLASSPATH = "ipan.builder.project.classpath";
	public static final String VIEW_FORM_COLS = "ipan.builder.view.formCols";
	public static final String ENTITY_EXPORT = "ipan.builder.entity.export";
	public static final String ENTITY_CLASSPATH = "ipan.builder.entity.classpath";
	public static final String TEMPLATE_CLASSPATH = "ipan.builder.template.classpath";
	public static final String JDBC_PASSWORD = "jdbc.password";
	public static final String JDBC_USERNAME = "jdbc.username";
	public static final String JDBC_URL = "jdbc.url";
	public static final String JDBC_DRIVER = "jdbc.driver";
	public static final String CONFIG_FILE = "ipan_cfg.properties";
	public static final String BUILD_PATTERN = "ipan\\.build\\.(\\w+(\\.\\w+)*)$";
	public static final String COMMON_PATTERN = "ipan\\.builder\\.common\\.(\\w+)$";
	public static final String SEP_CHAR = "/";
	private static BuilderCfg INSTANCE = new BuilderCfg();
	private String driver;
	private String url;
	private String username;
	private String password;
	private String templateClassPath = "code_template";
	private String entityExportPath;
	private String entityClassPath;
	private String projectClassPath;
	private int formCols = 2;
	private Map<String, BuildCfg> buildCfgContext = new HashMap<String, BuildCfg>();
	private Map<String, Object> commonVarMap = new HashMap<String, Object>();
	
	public static BuilderCfg getInstance() {
		return INSTANCE;
	}
	
	public void loadFile(File file) {
		InputStream fin = null;
		try {
			fin = new FileInputStream(file);
			Properties pro = new Properties();
			pro.load(fin);
			// 属性设置
			this.driver = pro.getProperty(JDBC_DRIVER);
			this.url = pro.getProperty(JDBC_URL);
			this.username = pro.getProperty(JDBC_USERNAME);
			this.password = pro.getProperty(JDBC_PASSWORD);
			// 支持hibernate的配置
			loadHibernateCfg(pro);
			if (BdStringUtils.isNotBlank(pro.getProperty(TEMPLATE_CLASSPATH))) {
				setTemplateClassPath(pro.getProperty(TEMPLATE_CLASSPATH).replace("\\", SEP_CHAR));
			}
			this.entityClassPath = pro.getProperty(ENTITY_CLASSPATH);
			if (pro.getProperty(PROJECT_CLASSPATH) != null) {
				this.projectClassPath = pro.getProperty(PROJECT_CLASSPATH);
			} else {
				this.projectClassPath = BdStringUtils.substringBeforeLast(this.entityClassPath, ".");
			}
			if (BdStringUtils.isNotBlank(pro.getProperty(ENTITY_EXPORT))) {
				setEntityExportPath(pro.getProperty(ENTITY_EXPORT).replace("\\", SEP_CHAR));
			}
			if (BdStringUtils.isNotBlank(pro.getProperty(VIEW_FORM_COLS))) {
				this.formCols = Integer.parseInt(pro.getProperty(VIEW_FORM_COLS));
			}
			// 遍历Key
			Enumeration<Object> em = pro.keys();
			Pattern buildPattern = Pattern.compile(BUILD_PATTERN);
			Pattern commonPattern = Pattern.compile(COMMON_PATTERN);
			while (em.hasMoreElements()) {
				// 模板文件配置设置
				String key = (String) em.nextElement();
				Matcher buildMatcher = buildPattern.matcher(key);
				if (buildMatcher.find() && !key.toLowerCase().endsWith(BuildCfg.PATH_MODE_PATH)) {
					String buildDir = buildMatcher.group(1).replace("\\", SEP_CHAR);
					// 创建BuildCfg
					BuildCfg cfg = new BuildCfg();
					cfg.setFilesDir(buildDir);
					cfg.setFilesByString(pro.getProperty(key));
					if (pro.getProperty(key + BuildCfg.PATH_MODE_PATH) != null) {
						cfg.setExport(pro.getProperty(key + BuildCfg.PATH_MODE_PATH));
					}
					buildCfgContext.put(key, cfg);
				}
				
				// 公共常量
				Matcher commonMatcher = commonPattern.matcher(key);
				if (commonMatcher.find()) {
					String var = commonMatcher.group(1);
					String value = pro.getProperty(key);
					commonVarMap.put(var, value);
				}
			}
			
		} catch (IOException e) {
			throw new RuntimeException("Config配置文件加载失败！", e);
		} finally {
			if (fin != null) {
				try {
					fin.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	private BuilderCfg() {
		File file = null;
		URL url = BdClassLoaderUtils.getResource(CONFIG_FILE, BuilderCfg.class);
		if (url == null) {
			return ;
		}
		file = new File(url.getFile());
		loadFile(file);
	}
	
	public List<BuildCfg> getBuildCfgList() {
		return new ArrayList<BuildCfg>(buildCfgContext.values());
	}
	
	public BuildCfg getBuildCfg(String key) {
		return buildCfgContext.get(key);
	}
	
	public Map<String, BuildCfg> getBuildCfgContext() {
		return new HashMap<String, BuildCfg>(buildCfgContext);
	}

	public Map<String, Object> getCommonVarMap() {
		return new HashMap<String, Object>(commonVarMap);
	}
	
	public Object getCommonVar(String key) {
		return commonVarMap.get(key);
	}

	public String getDriver() {
		return driver;
	}

	public void setDriver(String driver) {
		this.driver = driver;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getTemplateClassPath() {
		return templateClassPath;
	}

	public void setTemplateClassPath(String dir) {
		assertNotBlank(dir, "模板文件目录");
		this.templateClassPath = dir;
		this.templateClassPath = appendSepBefore(this.templateClassPath);
		this.templateClassPath = appendSepAfter(this.templateClassPath);
	}

	public String getEntityExportPath() {
		return entityExportPath;
	}

	public void setEntityExportPath(String entityDir) {
		assertNotBlank(entityDir, "实体文件目录");
		this.entityExportPath = appendSepAfter(entityDir);
	}
	
	public String getEntityClassPath() {
		return entityClassPath;
	}

	public void setEntityClassPath(String entityClassPath) {
		this.entityClassPath = entityClassPath;
	}

	public int getFormCols() {
		return formCols;
	}

	public void setFormCols(int formCols) {
		this.formCols = formCols;
	}
	
	public String getProjectClassPath() {
		return projectClassPath;
	}

	public void setProjectClassPath(String projectClassPath) {
		this.projectClassPath = projectClassPath;
	}

	private static void assertNotBlank(String param, String msg) {
		if (BdStringUtils.isBlank(param)) {
			throw new IllegalArgumentException("参数：" + msg + "，不能为空.");
		}
	}
	
	private static String appendSepBefore(String dir) {
		return (dir != null && !dir.startsWith(SEP_CHAR)) ? SEP_CHAR + dir : dir;
	}
	
	private static String appendSepAfter(String dir) {
		return (dir != null && !dir.endsWith(SEP_CHAR)) ? dir + SEP_CHAR : dir;
	}
	
	// 支持使用hibernate的配置连接（注意：两个同时配置的时候，以JDBC的配置为主；）
	private void loadHibernateCfg(Properties pro) {
		if (StringUtils.isBlank(this.driver)) {
			String driverClass = pro.getProperty(Environment.DRIVER);
			if (driverClass != null) {
				this.driver = driverClass;
			}
		}
		if (StringUtils.isBlank(this.url)) {
			String urlStr = pro.getProperty(Environment.URL);
			if (urlStr != null) {
				this.url = urlStr;
			}
		}
		if (StringUtils.isBlank(this.username)) {
			String userStr = pro.getProperty(Environment.USER);
			if (userStr != null) {
				this.username = userStr;
			}
		}
		if (StringUtils.isBlank(this.password)) {
			String passStr = pro.getProperty(Environment.PASS);
			if (passStr != null) {
				this.password = passStr;
			}
		}
	}

	/**
	 * 待生成的模板配置信息
	 * 
	 * 例如：
	 * filesDir=action/
	 * files=${name1}Action.java.ftl
	 * export=c:/workspace/src/main/java/com/ipan/project/actions/demo/
	 * 
	 * @author iPan
	 * @version 2013-11-27
	 */
	public static class BuildCfg {
		/** 绝对路径模式 */
		public static final String PATH_MODE_PATH = ".export";

		/** 模板相对目录 */
		private String filesDir;

		/** 模板名称 */
		private String[] files;

		/** 生成文件绝对路径 */
		private String export;

		public String[] getFiles() {
			return files;
		}
		
		public void setFiles(String[] files) {
			this.files = files;
		}

		public void setFilesByString(String files) {
			this.files = files.split(",");
		}

		public String getExport() {
			return export;
		}

		public void setExport(String path) {
			assertNotBlank(path, "生成文件目录");
			this.export = appendSepAfter(path);
		}
		
		public String getFilesDir() {
			return filesDir;
		}

		public void setFilesDir(String filesDir) {
			assertNotBlank(filesDir, "模板文件目录");
			this.filesDir = appendSepAfter(filesDir.replace(".", SEP_CHAR));
			
		}

		// 获取模板文件绝对路径
		public String getFullTemplatePath(String name) {
			String tempDir = BuilderCfg.getInstance().getTemplateClassPath();
			return tempDir + filesDir + name;
		}
		
		// 获取模板文件相对路径
		public String getRelativeTemplatePath(String name) {
			return filesDir + name;
		}
		
		// 获取生成文件绝对路径
		public String getFullCreatePath(String name) {
			return export + compileFileName(name);
		}
		
		// 获取生成文件的类路径
		public String getCreateClassPath() {
			String ep = getExport();
			String cp = BuilderCfg.getInstance().getProjectClassPath().replace(".", SEP_CHAR); // 根路径
			if (ep.endsWith(SEP_CHAR)) {
				ep = ep.substring(0, ep.length() - 1);
			}
			if (BdStringUtils.isBlank(ep)) {
				throw new IllegalArgumentException("获取模板文件生成目录失败.");
			}
			ep = (ep.lastIndexOf(cp) > -1) ? ep.substring(ep.lastIndexOf(cp)) : "";
			return ep.replace(SEP_CHAR, ".");
		}
		
		public File createFile(String fileName) {
			String fName = getFullCreatePath(fileName);
			File file = BdFileUtils.createFileByPath(fName);
			return file;
		}
		
		private static String compileFileName(String fileName) {
			return (fileName.toLowerCase().endsWith(".ftl")) ? fileName.substring(0, fileName.length() - 4) : fileName;
		}

		@Override
		public String toString() {
			return "BuildCfg [filesDir=" + filesDir + ", files=" + Arrays.toString(files) + ", export=" + export + "]";
		}

	}
	
}
